package br.com.sgi.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.springframework.transaction.annotation.Transactional;

import br.com.sgi.autentication.UsuarioLogado;
import br.com.sgi.dao.DaoGenerico;



public abstract class DaoGenericoImpl<T> implements DaoGenerico<T> {

	private static final long serialVersionUID = 6397851086603565823L;

	@PersistenceContext
	private EntityManager entityManager;
	
	private Class<T> persistentClass;
	
	
	 public DaoGenericoImpl() {
	        Type t = getClass().getGenericSuperclass();
	        ParameterizedType pt = (ParameterizedType) t;
	        persistentClass = (Class) pt.getActualTypeArguments()[0];
	    }
	

	public void setPersistentClass(Class<T> persistentClass) {
		this.persistentClass = persistentClass;
	}

	@Transactional
	public void save(T obj) {
		this.entityManager.persist(obj);
		this.entityManager.flush();
		
	}

	public final Class<T> getPersistentClass() {
		if (persistentClass == null) {
			throw new RuntimeException("É necessário invocar o método setPersistentClass(Class<T> clazz)");
		}
		return persistentClass;
	}
	
	public List<T> findAll() {
		Query query = this.entityManager.createQuery(" FROM " + getPersistentClass().getSimpleName());
		return query.getResultList();
	}
	
	public List<T> findByParam(String nameQuery, String parans) {
		Query query = this.entityManager.createNamedQuery(nameQuery).setParameter("nome", parans);
		return query.getResultList();
	}
	
	public T findById(Serializable id) {
		return this.entityManager.find(getPersistentClass(), id);
	}
	
	@Transactional
	public <T> T update(T obj) {
		return  (T) this.entityManager.merge(obj);
	}

	@Transactional
	public void deleteById(Serializable id) {
		this.entityManager.remove(findById(id));
		
	}
	
	public T findByNamedId(String queryname, Map<String, Object> params) {
		Query query = this.entityManager.createNamedQuery(queryname);	
		params.put("coSistema", UsuarioLogado.getCoSistema());
		setQueryParams(query, params);
		List list = query.getResultList();
		
		if(list != null && list.size() == 1)
			return (T) list.get(0);
		
		return null;
			
	}
	
	public List<T> findByNamedParams(String queryname, Map<String, Object> params) {
		Query query = this.entityManager.createNamedQuery(queryname);	
		params.put("coSistema", UsuarioLogado.getCoSistema());
		setQueryParams(query, params);
		return query.getResultList();
	}
	
	@Override
	public List<T> findByNamedParams(String queryname) {
		Query query = this.entityManager.createNamedQuery(queryname);	
		Map<String, Object>  params = new java.util.HashMap<String, Object>();
		params.put("coSistema", UsuarioLogado.getCoSistema());
		setQueryParams(query, params);
		return query.getResultList();
	}
	
	
	/**
	 * Atribuir os parÃ¢metros nomeados da query.
	 * 
	 * @param query
	 * @param params
	 */
	private void setQueryParams(Query query, Map<String, Object> params) {
		for (Entry<String, Object> entry : params.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
	}
	
	public List<T> findByNativeQuery(String sqlString,
			Map<String, Object> params) {
		Query query = this.entityManager.createQuery(sqlString);
		params.put("coSistema", UsuarioLogado.getCoSistema());
		setQueryParams(query, params);
		return query.getResultList();
	}
	

	public T getReference(Long id) {
		EntityManager em = getEntityManager();
		return em.getReference(persistentClass, id);
	}


	public EntityManager getEntityManager() {
		return entityManager;
	}


	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	@Transactional
	public Criteria getCriteria(){
		Session session = (Session)getEntityManager().getDelegate(); 
		return session.createCriteria(persistentClass);
		
	}
	

	public List<T> findByNamedParamsNotIdSystem(String queryname, Map<String, Object> params) {
		Query query = this.entityManager.createNamedQuery(queryname);	
		setQueryParams(query, params);
		return query.getResultList();
	}
	
	
}
